#pragma once
/**
 * @file HttpServer.hpp
 * @author 1102403684@qq.com
 * @brief 实现Http服务器的搭建
 * @version 0.1
 * @date 2024-10-30
 *
 * @copyright Copyright (c) 2024
 *
 */

#include "../common/logger.hpp"
#include "HttpRequest.hpp"
#include "HttpResponse.hpp"
#include "HttpContext.hpp"
#include "../src/TcpServer.hpp"

#include <functional>
#include <unordered_map>

class HttpServer
{
public:
    using Handler = std::function<void(const HttpRequest &request, HttpResponse &response)>;
    using Handlers = std::unordered_map<std::shared_ptr<std::regex>, Handler>;/*存储一个编译好的regex而不是字符串*/
    HttpServer(int port, int timeout = 10) : server_(port)
    {
        server_.enbale_inactive_release(timeout);//默认开启保护机制
        server_.set_connected_callback(std::bind(&HttpServer::on_connected, this, std::placeholders::_1));
        server_.set_message_callback(std::bind(&HttpServer::on_message, this, std::placeholders::_1, std::placeholders::_2));
    }

    void set_basedir(const std::string path)
    {
        base_dir_ = path;
    }

    void Get(const std::string &pattern, const Handler &handler)
    {
        get_router_.insert(std::make_pair(std::make_shared<std::regex>(pattern), handler)); 
    }
    void Post(const std::string &pattern, const Handler &handler)
    {
        post_router_.insert(std::make_pair(std::make_shared<std::regex>(pattern), handler)); 
    }
    void Put(const std::string &pattern, const Handler &handler)
    {
        put_router_.insert(std::make_pair(std::make_shared<std::regex>(pattern), handler)); 
    }
    void Delete(const std::string &pattern, const Handler &handler)
    {
        delete_router_.insert(std::make_pair(std::make_shared<std::regex>(pattern), handler)); 
    }

    void set_thread_count(int count)
    {
        server_.set_thread_count(count);
    }

    void start()
    {
        server_.start();
    }

private:
    /**
     * @brief 序列化Response并发送
     *
     * @param conn
     * @param request
     * @param response
     */
    void write_response(const Connection::PtrConnection &conn, const HttpRequest &request, HttpResponse &response)
    {
        //1 完善头部字段
        if(!request.is_keep_alive()){
            response.set_header("Connection", "close");
        }else {
            response.set_header("Connection", "keep-alive");
        }
        //确保body不为空 且 没有设置正文长度，我们才补充字段
        if(!response.body_.empty() && !response.has_header("Content-Length"))
        {
            response.set_header("Content-Length", std::to_string(response.body_.size()));
        }

        if(!response.body_.empty() && !response.has_header("Content-Type")){
            response.set_header("Content-Type", "application/octet-stream");
        }
        
        if(response.is_redirect_){
            response.set_header("Location", response.redirect_url_);
        }

        //2 将rsp中的要素 按照http协议的格式进行组织
        std::string response_str;
        response_str += request.get_version() + " " + std::to_string(response.statu_) + " " + Utils::get_statu_describe(response.statu_) + "\r\n";
        for(auto& head: response.headers_){
            response_str += head.first + ": " + head.second + "\r\n";
        }
        response_str+="\r\n";
        response_str+= response.body_;
        //3 发送数据
        conn->send(response_str.c_str(), response_str.size());
    }

    /**
     * @brief 将静态资源读取后放入body中，并设置Content-Type
     * 
     * @param request 
     * @param response 
     * @return true 
     * @return false 
     */
    bool file_handler(const HttpRequest &request, HttpResponse &response)
    {
        bool ret = Utils::read_file(request.path_, response.body_);
        if(ret == false){
            return false;
        }

        std::string mime = Utils::get_ext_mime(request.path_);
        response.set_header("Content-Type", mime);
        return true;
    }

    void error_handler(const HttpRequest &request, HttpResponse &response)
    {
        //1 组织一个错误展示页面  
        std::string body;
        body += "<html>";
        body += "<head>";
        body += "<meta http-equiv='Content-Type' content='text/html'; charset=utf-8>";
        body += "</head>";
        
        body += "<body>";
        body += "<h1>";
        body += std::to_string(response.statu_);
        body += " ";
        body += Utils::get_statu_describe(response.statu_);
        body += "</h1>";
        body += "</body>";

        body += "</html>";

        //2 将页面数据当作响应正文放入rsp中
        response.set_content(body, "text/html");

    }

    /**
     * @brief 
     * @details 
     * - 路由表内：正则表达式-处理函数，只要正则匹配成功就调用处理函数
     * @param request 
     * @param response 
     * @param handlers 
     */
    void dispatcher(HttpRequest &request, HttpResponse &response, Handlers& handlers)
    {
        for(auto& handler: handlers){
            const std::regex& re = *(handler.first);
            Handler& functor = handler.second;
            bool ret = std::regex_match(request.path_,request.match_, re);
            if(ret == false){
                continue;
            }

            functor(request, response);
            return;
        }
        
        response.statu_ = 404; //not found
    }

    /**
     * @brief 判断请求是否是静态资源请求
     * 
     * @param request 
     * @return true 
     * @return false 
     */
    bool is_file_request(HttpRequest &request)
    {
        //1 必须设置了静态资源根目录 
        if(base_dir_.empty()) return false;
        //2 请求方法必须是 GET | HEAD 请求
        if(request.method_ != "GET" && request.method_ != "HEAD"){
            return false;
        }
        //3 请求资源路径必须合法
        bool ret = Utils::is_valid_path(request.path_);
        if(ret == false){
            return false;
        }
        //4 请求的资源必须存在, 需要排除 / 根目录
        std::string real_path = base_dir_ + request.path_;
        if(request.path_ == "/"){
            real_path += "index.html";
        }

        if(!Utils::is_regular(real_path)){
            return false;
        }

        request.path_ = real_path;
        return true;
    }

    /**
     * @brief 路由
     * @details 首先判断是否是静态资源请求，是直接调用file_handler 否则调用dispatcher
     * @param request
     * @param response
     */
    void route(HttpRequest &request, HttpResponse &response)
    {
        if(is_file_request(request)){
            file_handler(request, response);
            return;
        }

        if(request.get_method() == "GET" || request.get_method() == "HEAD"){
            return dispatcher(request, response, get_router_);
        }else if(request.get_method() == "POST"){
            return dispatcher(request, response, post_router_);
        }else if(request.get_method() == "PUT"){
            return dispatcher(request, response, put_router_);
        }else if(request.get_method() == "POST"){
            return dispatcher(request, response, delete_router_);
        }

        response.statu_ = 405; //不支持的请求方法
    }
private: /*设置给TCPServer的回调函数*/
    void on_connected(const Connection::PtrConnection &conn)
    {
        conn->set_context(HttpContext());
        LOG_DEBUG("new connection {}", conn->get_connect_id());
    }

    void on_message(const Connection::PtrConnection &conn, Buffer *buffer)
    {
        while (buffer->get_readable_size() > 0)
        {
            // 1 获取上下文
            HttpContext *context = conn->get_context()->get<HttpContext>();

            // 2 解析上下文得到request对象
            context->recv_and_parse(buffer);
            HttpRequest &request = context->get_request();
            HttpResponse response(context->get_resp_statu());
            // 注意如果解析出错 则将message回调置为空
            // 避免解析请求行出错->调用shutdown->调用messgae_callback->再次出错(因为此时recv_statu为error)的循环
            // 所以这里采用了较为激进的做法 一旦解析错误 直接断开连接返回
            if(response.statu_ >= 400)/*recv_statu_ == error 也可以*/
            {
                error_handler(request, response);
                write_response(conn, request, response);
                conn->set_message_callback(nullptr);
                conn->shutdown();
                return;
            }   
            if (context->get_recv_statu() != HttpRecvStatus::RECV_OVER)
            {
                return;
            }

            // 3 请求路由 + 业务处理
            route(request, response);
            // 3.1 如果响应码出错
            if (response.statu_ >= 400)
            {
                error_handler(request, response);
                write_response(conn, request, response);
                conn->shutdown();
                return;
            }
            // 4 组织HttpResponse并组织发送
            write_response(conn, request, response);

            //千万不能先重置上下文，否则无法正确判断连接的状态，因为上下文重置的同时也会重置请求
            // 6 看是否是长连接，不是则关闭连接
            if (!request.is_keep_alive()){
                conn->shutdown();
            }

            // 5 重置上下文
            context->reset();
        }
    }

private:
    Handlers get_router_;
    Handlers post_router_;
    Handlers put_router_;
    Handlers delete_router_;
    std::string base_dir_;
    TcpServer server_;
};
